package com.bigboss.knowstar.config;

import com.bigboss.knowstar.entity.User;
import com.bigboss.knowstar.entity.model.APIResult;
import com.bigboss.knowstar.service.impl.UserServiceImpl;
import com.bigboss.knowstar.utils.JWTUtil;
import com.bigboss.knowstar.utils.LoginAuthenticationFilter;
import com.bigboss.knowstar.utils.VerificationCodeFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserServiceImpl userService;

//    @Autowired
//    VerificationCodeFilter verificationCodeFilter;

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    LoginAuthenticationFilter loginAuthenticationFilter() throws Exception {
        LoginAuthenticationFilter filter = new LoginAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setFilterProcessesUrl("/login");
        filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
                User principal = (User) auth.getPrincipal();
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                resp.setStatus(200);
                Map<String, Object> map = new HashMap<>();
                Map<String, String> payload = new HashMap<>();
                payload.put("Uid", principal.getUid().toString());
                String token = JWTUtil.generateToken(payload);
                map.put("status", "success");
                map.put("token", token);
                map.put("data", principal);
                out.write(new ObjectMapper().writeValueAsString(map));
                out.flush();
                out.close();
            }
        });
        filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                Map<String, Object> map = new HashMap<>();
                map.put("status", "error");
                if (e instanceof LockedException) {
                    map.put("data", "账户被锁定，登录失败");
                } else if (e instanceof BadCredentialsException) {
                    map.put("data", "用户名或密码错误，登录失败");
                } else if (e instanceof DisabledException) {
                    map.put("data", "账户被禁用，登录失败");
                } else if (e instanceof AuthenticationServiceException) {
                    map.put("data", "验证码错误");
                } else {
                    map.put("data", "登录失败");
                }
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        });
        return filter;
    }

    // 认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    // 授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 判断验证码是否正确
//        http.addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class);
        // 重写登录验证逻辑
        http.addFilterAt(loginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
//                .antMatchers("/**")
//                .permitAll()
                //过滤不需要认证的路径
//                .anyRequest().authenticated()
                // 对任何一个请求，都需要认证
                .and()
//                .formLogin()
                /**
                 * 我们配置的登录页面是一个login_page
                 * 但实际上login_page并不是一个页面
                 * 而是返回一段JSON
                 * 这是因为当我未登录就去访问其他页面时
                 * Spring Security会自动跳转到到login_page页面
                 * 但是在Ajax请求中，不需要这种跳转
                 * 我要的只是是否登录的提示，所以这里返回json即可
                 */
//                .loginPage("/login_page")
//                .loginProcessingUrl("/doLogin")
//                .usernameParameter("UStuId")
//                .passwordParameter("Upassword")
//                .successHandler(new AuthenticationSuccessHandler() {
//                    @Override
//                    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
////                      Object principal = auth.getPrincipal();
//                        resp.setContentType("application/json;charset=utf-8");
//                        PrintWriter out = resp.getWriter();
//                        resp.setStatus(200);
//                        Map<String, Object> map = new HashMap<>();
//                        map.put("status", "success");
//                        map.put("data", "登录成功");
//                        ObjectMapper om = new ObjectMapper();
//                        out.write(om.writeValueAsString(map));
//                        out.flush();
//                        out.close();
//                    }
//                })
//                .failureHandler(new AuthenticationFailureHandler() {
//            @Override
//            public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
//                resp.setContentType("application/json;charset=utf-8");
//                PrintWriter out = resp.getWriter();
//                resp.setStatus(401);
//                Map<String, Object> map = new HashMap<>();
//                map.put("status", "error");
//                if (e instanceof LockedException) {
//                    map.put("data", "账户被锁定，登录失败");
//                } else if (e instanceof BadCredentialsException) {
//                    map.put("data", "用户名或密码错误，登录失败");
//                } else if (e instanceof DisabledException) {
//                    map.put("data", "账户被禁用，登录失败");
//                } else {
//                    map.put("data", "登录失败");
//                }
//                ObjectMapper om = new ObjectMapper();
//                out.write(om.writeValueAsString(map));
//                out.flush();
//                out.close();
//            }
//        })
//                .and()
                .logout()
                .logoutUrl("/logout")
                .clearAuthentication(true)
                .invalidateHttpSession(true)
//                .addLogoutHandler(new LogoutHandler() {
//                    @Override
//                    public void logout(HttpServletRequest req, HttpServletResponse rep, Authentication auth) {
//                    }
//                })
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        resp.setStatus(200);
                        Map<String, Object> map = new HashMap<>();
                        map.put("status", "success");
                        map.put("data", "退出登录成功");
                        ObjectMapper om = new ObjectMapper();
                        out.write(om.writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                .csrf().disable().exceptionHandling()
                // 没有认证时，在这里处理结果，不要重定向
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        resp.setStatus(401);
                        PrintWriter out = resp.getWriter();
                        Map<String, Object> map = new HashMap<>();
                        if (e instanceof InsufficientAuthenticationException) {
                            map.put("status", "error");
                            map.put("data", "请求失败，请联系管理员");
                        }
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                });
    }

    // 忽略拦截
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login");
    }
}
